﻿using Microsoft.Practices.Unity.Utility;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Text;
using System.Threading;

namespace Artech.Core.Wcf
{
    public class ContextReceiver: ICallContextInitializer
    {
        private HeaderNameResolver resolver;
        public string HeaderName { get; private set; }
        public string Namespace { get; private set; }
        public bool TransactionScopeRequired { get; private set; }

        public ContextReceiver(string headerName, string ns, bool transactionScopeRequired)
        {
            Guard.ArgumentNotNullOrEmpty(headerName, "headerName");
            Guard.ArgumentNotNullOrEmpty(ns, "ns");

            this.HeaderName = headerName;
            this.Namespace = ns;
            this.TransactionScopeRequired = transactionScopeRequired;
            this.resolver = new HeaderNameResolver(ns);
        }

        public void AfterInvoke(object correlationState)
        {
            ApplicationContext.Current.Items.Clear();
            CultureInfo[] cultures = (CultureInfo[])correlationState;
            Thread.CurrentThread.CurrentCulture = cultures[0];
            Thread.CurrentThread.CurrentUICulture = cultures[1];
        }

        public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
        {
            CultureInfo[] cultures = new CultureInfo[] { Thread.CurrentThread.CurrentCulture, Thread.CurrentThread.CurrentUICulture };
            IEnumerable<ContextItem> items = this.ReceiveContext(message);
            ApplicationContext.Current.Provider.SetContextItems(new ContextItemCollection(items));

            Thread.CurrentThread.CurrentCulture = ApplicationContext.Current.CurrentCulture;
            Thread.CurrentThread.CurrentUICulture = ApplicationContext.Current.CurrentUICulture;
            if (string.IsNullOrEmpty(ApplicationContext.Current.TransactionId) && this.TransactionScopeRequired)
            {
                ApplicationContext.Current.TransactionId = Guid.NewGuid().ToString();
            }
            return cultures;
        }

        protected virtual IEnumerable<ContextItem> ReceiveContext(Message request)
        {
            Guard.ArgumentNotNull(request, "request");
            if (request.Version.Envelope == EnvelopeVersion.Soap11 || request.Version.Envelope == EnvelopeVersion.Soap12)
            {
                ContextItem[] items = request.Headers.GetHeader<ContextItem[]>(this.HeaderName, this.Namespace);
                if (null != items)
                {
                    foreach (ContextItem item in items)
                    {
                        yield return item;
                    }
                }

                var contextHeaderNames = from header in request.Headers
                                         where header.Name != this.HeaderName && header.Namespace == this.Namespace
                                         select header.Name;
                foreach (var headerName in contextHeaderNames)
                {
                    string value = request.Headers.GetHeader<string>(headerName, this.Namespace);
                    yield return new ContextItem(headerName, value);
                }
            }
            else
            {
                HttpRequestMessageProperty requestProperty = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
                if (null != requestProperty)
                {
                    IEnumerable<string> contextHeaderNames = resolver.GetAllHeaderNames(requestProperty.Headers.AllKeys);
                    foreach (string headerName in contextHeaderNames)
                    {
                        yield return new ContextItem(resolver.GetName(headerName), requestProperty.Headers[headerName]);
                    }
                }
            }
        }
    }
}